package monitorcon

import (
	"log"
	"monitor/models"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"time"
	//"github.com/go-redis/redis"
	"encoding/json"
	"github.com/streadway/amqp"
	"strconv"
	"strings"
)

//postgres db connection param
const (
	Host     = "localhost"
	Port     = 5432
	User     = "postgres"
	Password = "fqc187"
	Dbname   = "postgres"
)

//redis manager
var rsmanager *RedisManager

//实例 manager
var mon *InstanceManager

//Element ...
type Element interface{}

//List ...
type List []Element

//GetrsManager 返回redis manager
func GetrsManager() *RedisManager {
	return rsmanager
}

//GetinstanceManager 返回实例 manager
func GetinstanceManager() *InstanceManager {

	return mon
}

//SetrsManager 设置redis manager
func SetrsManager(rs *RedisManager) {
	rsmanager = rs
}

//SetInstanceManager 设置实例 manager
func SetInstanceManager(m *InstanceManager) {

	mon = m
}

//批量采集所有设备数据管理入口
func datamanagementstart(rs *RedisManager) {
	var flag = timingjudge(rs)

	if flag == false {
		return
	}

	prepareforInsnode(rs)

}

//寻找有设备管理的实例节点
func prepareforInsnode(rs *RedisManager) {

	var i, tokennum int
	var connindex string
	livenum, err := rs.GetSinglefield(LIVECONNNUMLABEL)
	if err != nil {
		log.Println(err)
		return
	}

	ilivenum, err := strconv.Atoi(livenum)
	if err != nil {
		log.Println(err)
		return
	}

	for i = 1; i <= ilivenum; i++ {

		connindex = strconv.Itoa(i)                        //MLABEL +
		tnum, err := rs.GetInstancefield(connindex, TOKEN) //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//fmt.Printf("%s token is no existed\n",connindex)
			continue
		}

		tokennum, err = strconv.Atoi(tnum)

		if err != nil {
			log.Println(err)
			return
		}

		if tokennum == -1 {
			continue //是空闲连接，无关联设备
		}

		getinterfacedata(i, rs, 1) // get data from http server and save to db

	}
}

//时间周期判断，如每2分钟采集一次数据
func timingjudge(rs *RedisManager) bool {
	beftime, err := rs.GetSinglefield(DATAPERIODLABEL)
	if err != nil { //first use data manager,保存当前时间
		//currentTime := time.Now()
		strt := strconv.FormatInt(time.Now().Unix(), 10) //变成10进制
		//log.Println("now time=", strt)
		rsmanager.SetSinglefield(DATAPERIODLABEL, strt)

		return false
	}

	//log.Printf("bef=%s", beftime)

	//d2:=time.Now().Unix()
	d2 := strconv.FormatInt(time.Now().Unix(), 10) //变成10进制
	d3, err := strconv.Atoi(d2)

	//  计算两个时间差
	bef, err := strconv.Atoi(beftime)
	//log.Printf("bef=%d", bef)
	subt := d3 - bef

	//log.Printf("data time diff is %d秒", subt)

	dtime := beego.AppConfig.String("dataperiod") //周期时间，单位秒

	tt, err := strconv.Atoi(dtime)
	if err != nil {
		log.Println("data period time is not set, deafult time is 60s")
		//panic(err)
		tt = 60
	}

	//如果时间差大于周期时间，返回true
	if subt >= tt {
		//log.Println("next time=", d2)
		rsmanager.SetSinglefield(DATAPERIODLABEL, d2) //保存下次的时间到redis

		return true
	}

	return false

}

//采集接口数据入口
//fromtype=0: from instance manager 需要存取到redis
//fromtype=1: from data manager  需要存取到postrges db

//从底层得到数据 ，发给Redis server ,并且保存入db,如果存在订阅，则查看内容是否有变化，如有变化，发给订阅者
func getinterfacedata(index int, rs *RedisManager, fromtype int) {

	connindex := strconv.Itoa(index)                    //MLABEL +
	num, err := rs.GetInstancefield(connindex, HOSTNUM) //得到实例下的设备数

	if err != nil {
		//fmt.Printf("%s token is no existed\n",connindex)
		return
	}

	hostnum, err := strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if hostnum == -1 {
		return //没有可分配的设备
	}

	//得到订阅field list
	fieldmap := rs.Getsubscribedfieldlists()
	fielditemmap := rs.Getsubscribedfielditemlists()

	var i int
	for i = 1; i <= hostnum; i++ {

		//hi := strconv.Itoa(i)

		//hostinfo, err := rs.HGet(connindex, "hid"+hi).Result() //得到token ,如果token=-1,则是空闲的连接
		hostinfo, err := rs.GetValueFromList(connindex, i-1)
		if err != nil {

			continue
		}

		s := strings.Split(hostinfo, ",")
		log.Printf("device %s,%s\n", s[0], s[1])

		if s[1] == "1" { //本设备需要采集数据

			var resp string
			resp = GetStackinfo(s[0]) //从模拟服务器返回stack信息
			log.Println(resp)
			senddata(s[0], "stack", resp, fromtype, rs, fieldmap)

			resp = GetPortInfo(s[0]) //port信息
			log.Println(resp)
			senddata(s[0], "port", resp, fromtype, rs, fieldmap)

			posmap := parsePos(resp, "port")
			respposmap := getposdata(s[0], posmap, "port")
			sendposdata(s[0], "port", respposmap, fromtype, rs, fielditemmap)

			resp = GetInterfaceInfo(s[0]) //interface信息
			log.Println(resp)
			senddata(s[0], "interface", resp, fromtype, rs, fieldmap)

			posmap = parsePos(resp, "interface")
			respposmap = getposdata(s[0], posmap, "interface")
			sendposdata(s[0], "interface", respposmap, fromtype, rs, fielditemmap)

			resp = GetCPUInfo(s[0]) //cpu信息
			log.Println(resp)
			senddata(s[0], "cpu", resp, fromtype, rs, fieldmap)

			posmap = parsePos(resp, "cpu")
			respposmap = getposdata(s[0], posmap, "cpu")
			sendposdata(s[0], "cpu", respposmap, fromtype, rs, fielditemmap)

			resp = GetMemoryInfo(s[0]) //memory信息
			log.Println(resp)
			senddata(s[0], "memory", resp, fromtype, rs, fieldmap)

			posmap = parsePos(resp, "memory")
			respposmap = getposdata(s[0], posmap, "memory")
			sendposdata(s[0], "memory", respposmap, fromtype, rs, fielditemmap)

			resp = GetMacInfo(s[0]) //mac信息
			log.Println(resp)
			senddata(s[0], "mac", resp, fromtype, rs, fieldmap)

			posmap = parsePos(resp, "mac")
			respposmap = getposdata(s[0], posmap, "mac")
			sendposdata(s[0], "mac", respposmap, fromtype, rs, fielditemmap)

		}

	}

}

func senddata(hostuuid string, field string, resp string, fromtype int, rs *RedisManager, subscribefieldmap map[string]string) {
	//save data to redis server
	//log.Println("type=", fromtype)
	if fromtype == 0 {
		key := hostuuid + "||" + field
		fieldval := subscribefieldmap[key] //验证field是否存在订阅列表中
		if fieldval != "" {                //此field已被订阅，比较内容是否有变化
			//fval:redis的值,resp 从底层http得到的值
			fval := Gethostinfofromredis(hostuuid, field, rs)
			if fval != resp {
				//send data to mq queue
				msg := amqp.Publishing{
					DeliveryMode: amqp.Persistent,
					Timestamp:    time.Now(),
					ContentType:  "text/plain",
					Body:         []byte(resp),
				}

				//log.Println("send changed field resp info to mq")
				channel.Publish(EXCHANGEMONITOR, ROUTINGKEY, false, false, msg)

			}
		}

		//save 底层http的值 to redis
		Savehostinfotoredis(rs, hostuuid, field, resp)

	} else {

		//save data to db
		map2 := make(map[string]interface{})
		var err = json.Unmarshal([]byte(resp), &map2)
		//err = json.Unmarshal( []byte(s), &map2)

		if err != nil {
			log.Println("unmarshal json info failed")
			return

		}

		log.Println(map2)

		var arr [10]string                      //define 至多10个节点的数组
		arr[0] = ""                             //第一个根节点为空
		Parsemap(hostuuid, map2, arr, 1, field) //field as tablename
		//log.Println("end saving data")

	}

}

//Parsemap :struct and save to db
func Parsemap(hostuuid string, m map[string]interface{}, rootarr [10]string, index int, tablename string) {
	//对于k,v 来说，取1到index-1 的节点作为其root节点，root[0]预放""
	for k, v := range m {

		switch value := v.(type) {
		case string: // v : string

			savedata(hostuuid, rootarr, index, k, value, tablename) // taleid:1 for stackinfo

			//value:= v.(string)
		case map[string]interface{}: //v:map[vendorName:testname vendorPN:pn]
			rootarr[index] = k
			Parsemap(hostuuid, v.(map[string]interface{}), rootarr, index+1, tablename)
		case []interface{}: //map array:for example:map[vendorName:testname vendorPN:pn] map[firstName:Anna lastName:Smith]

			//log.Println("map array")
			for _, v2 := range value {
				switch vv := v2.(type) {
				case map[string]interface{}:
					log.Println(vv)
					//log.Printf("%s", k) //k as root
					rootarr[index] = k
					Parsemap(hostuuid, vv, rootarr, index+1, tablename)
				}
			}

		}
	}

}

//savedata to db:  root arr 对应数据库的node1,node2,node3...
func savedata(hostuuid string, rootarr [10]string, index int, key string, value string, tablename string) {
	var i int

	nodeinfo := new(models.Nodeinfo)
	//from 1,root[0]=""
	for i = 1; i < index; i++ {
		//log.Printf("%s\n", rootarr[i])
		if i == 1 {
			nodeinfo.Node1 = rootarr[i]
		} else if i == 2 {
			nodeinfo.Node2 = rootarr[i]
		}

	}

	if i == 1 {
		nodeinfo.Node1 = key
	} else if i == 2 {
		nodeinfo.Node2 = key
	} else if i == 3 {
		nodeinfo.Node3 = key
	}

	//log.Println(nodeinfo)
	//log.Printf("%s -> %s\n", key, value)

	orm.Debug = true
	o := orm.NewOrm()
	o.Using(Dbname) //use database name

	if tablename == "stack" { //stackinfo
		skinfo := new(models.Stackinfo)
		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	} else if tablename == "port" { //portinfo
		skinfo := new(models.Portinfo)
		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	} else if tablename == "interface" { //interfaceinfo
		skinfo := new(models.Interfaceinfo)

		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	} else if tablename == "cpu" { //cpuinfo
		skinfo := new(models.Cpuinfo)
		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	} else if tablename == "memory" { //memory
		skinfo := new(models.Memoryinfo)

		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	} else if tablename == "mac" { //mac
		skinfo := new(models.Macinfo)
		skinfo.Hostuuid = hostuuid

		skinfo.Node1 = nodeinfo.Node1
		skinfo.Node2 = nodeinfo.Node2
		skinfo.Node3 = nodeinfo.Node3
		skinfo.Value = value
		skinfo.Creationtime = time.Now()

		log.Println(o.Insert(skinfo))

	}

}

//Savehostinfotoredis hostuuid作为key，保存信息到redis
func Savehostinfotoredis(rs *RedisManager, hostuuid string, field string, info string) {
	map1 := make(map[string]interface{})
	map1[field] = info

	rs.SetDevicefield(hostuuid, map1)

	//log.Printf("save %s info to redis for %s", field, hostuuid)
}

//Gethostinfofromredis hostuuid作为key，从redis得到信息
func Gethostinfofromredis(hostuuid string, field string, rs *RedisManager) string {

	info, err := rs.GetDevicefield(hostuuid, field) //得到本设备某个field的值

	if err != nil {
		//log.Printf("getting %s info from redis error", field)
		return ""
	}

	return info

}

//分析底层HTTP server传来的内容，返回map struct
func parsePos(info string, field string) map[string]string {
	map1 := make(map[string]interface{})
	var err = json.Unmarshal([]byte(info), &map1)
	if err != nil {
		log.Printf("json parse err:%s", info)
		return nil

	}
	//信息格式:
	/*"Members": [
	    {
	        "@odata.id": "/redfish/v1/rich/NetworkService/Hosts/xxxx/Devm/Ports/10GE1%2F17%2F1",
	        "position": "10GE2/17/1"
	    },
	    {
	        "@odata.id": "/redfish/v1/rich/NetworkService/Hosts/xxxx/Devm/Ports/10GE1%2F17%2F2",
	        "position": "10GE2/17/2"
	    }
	]*/
	posmap := make(map[string]string)
	var value = (map1["Members"]) //这是关键词
	val, ok := value.([]interface{})
	if !ok {
		/*
		log.Println("It's not ok for type members array")
		str, ok2 := value.(string)
		if ok2 {
		   log.Println("members string:",str)
		}*/
		
		return nil
	}

	var posval interface{}

	for _, v2 := range val {
		switch vv := v2.(type) {
		case map[string]interface{}:

			//log.Println(vv);
			if field == "port" || field == "cpu" || field == "memory" {
				posval = vv["position"] //可能是id,iframe,这是关键词
			} else if field == "interface" {
				posval = vv["ifname"]
			} else if field == "mac" {
				posval = vv["id"]
			}
			//posval=vv["position"] //可能是id,iframe,这是关键词

			val, ok := posval.(string)
			if !ok {
				//log.Println("It's not ok for type value string")
				posmap[val]="error"
			}else{

			    posmap[val] = val
			}
		}
	}

	//var maplen = len(posmap)
	//log.Println("map len=", maplen)

	return posmap

}

//get content by hostuuid and posiiton
func getposdata(hostuuid string, posmap map[string]string, field string) map[string]string {
	respposmap := make(map[string]string)
	var resp string
	for k := range posmap {
		resp = ""
		if field == "port" {
			resp = GetPortByposition(hostuuid, k) //k就是position
		} else if field == "interface" {
			resp = GetInterfaceByfname(hostuuid, k)
		} else if field == "cpu" {
			resp = GetCPUInfoByPosition(hostuuid, k)
		} else if field == "memory" {
			resp = GetMemoryInfoByPosition(hostuuid, k)
		} else if field == "mac" {
			resp = GetMacInfoByid(hostuuid, k)
		}

		//k就是position,也就是field(port,cpu etc)的item (position ,id)
		//使用"_"代替"/"
		//log.Println("key bef:", k)
		k = strings.Replace(k, "/", "_", -1)
		//log.Println("key after:", k)
		
		respposmap[hostuuid+"||"+field+"||"+k] = resp
		
		log.Println(hostuuid+"||"+field+"||"+k)
	}

	return respposmap
}

//save position data to redis server
func sendposdata(hostuuid string, field string, respposmap map[string]string, fromtype int, rs *RedisManager, subscribefielditemmap map[string]string) {
	//save data to redis server
	//k=hostuuid+||+field+pos
	//v=response content
	for k, v := range respposmap {
		if fromtype == 0 {
			itemkey := k
			fieldval := subscribefielditemmap[itemkey] //验证field item是否存在订阅列表中
			//log.Printf("%s is in subscribe item list", fieldval)
			if fieldval != "" { //此field已被订阅，比较内容是否有变化

				fval := Gethostinfofromredis(hostuuid, itemkey, rs)
				//log.Printf("redis item val:%s ", fval)
				//log.Printf("new item val:%s ", v)
				if fval != v {
					//send data to mq queue
					msg := amqp.Publishing{
						DeliveryMode: amqp.Persistent,
						Timestamp:    time.Now(),
						ContentType:  "text/plain",
						Body:         []byte(v),
					}

					//log.Println("send changed field item resp info to mq")
					channel.Publish(EXCHANGEMONITOR, ROUTINGKEY, false, false, msg)

				}
			}

			Savehostinfotoredis(rs, hostuuid, k, v)
			//send data to mq queue

		} else {

			//save data to db
			map2 := make(map[string]interface{})
			var err = json.Unmarshal([]byte(v), &map2)
			//err = json.Unmarshal( []byte(s), &map2)

			if err != nil {
				log.Println("unmarshal json info failed")
				return

			}

			log.Println(map2)

			var arr [10]string                      //define 至多10个节点的数组
			arr[0] = ""                             //第一个根节点为空
			Parsemap(hostuuid, map2, arr, 1, field) //field as tablename
			//log.Println("end saving data")

		}

	}
}

//GetFieldinfo for北向的port,mac的field 信息进行分析，返回list
func GetFieldinfo(rs *RedisManager, hostuuid string, field string,ver string) List {
	var resp string
	var err error
	if field == "cpu" {
		resp, err = rs.GetDevicefield(hostuuid, "cpu") //cpu信息

	} else if field == "memory" {
		resp, err = rs.GetDevicefield(hostuuid, "memory") //memory信息
	} else if field == "port" {
		resp, err = rs.GetDevicefield(hostuuid, "port") //port信息
	} else if field == "mac" {
		resp, err = rs.GetDevicefield(hostuuid, "mac") //mac信息
	}
	if err != nil {
		return nil
	}

	log.Println("info:", resp)

	var posmap map[string]string
	if field == "cpu" {
		posmap = parsePos(resp, "cpu") //position info
	} else if field == "memory" {
		posmap = parsePos(resp, "memory") //position info
	} else if field == "port" {
		posmap = parsePos(resp, "port") //position info
	} else if field == "mac" {
		posmap = parsePos(resp, "mac") //position info
	}

	map2 := make(map[string]interface{})
	//var maparr [10]interface{}
	list := make(List, len(posmap))
    log.Println("len:",len(posmap))
  
	index := 0
	var s string
	for k := range posmap {
		log.Println("pos=", k)

		k = strings.Replace(k, "/", "_", -1) //"10/GE2/"->"10_GE_2"

		if field == "cpu" {
			s = GetCPUInfofromredis(rs, hostuuid, k)
		} else if field == "memory" {
			s = GetMemoryInfofromredis(rs, hostuuid, k)
		} else if field == "port" {
			s = GetPortfromredis(rs, hostuuid, k)
		} else if field == "mac" {
			s = GetMacInfofromredis(rs, hostuuid, k)
		}

		
		err := json.Unmarshal([]byte(s), &map2)
		if err != nil {
		    log.Println("pos info=", s)
			continue
		}

		map3 := make(map[string]interface{})
		if field == "cpu" {
			map3["position"] = map2["position"]
			map3["CpuUseRate"] = map2["systemCpuUsage"]
			//maparr[index]=map3
			list[index] = map3
		} else if field == "memory" {
			map3["position"] = map2["position"]
			map3["MemoryTotal"] = map2["osMemoryTotal"]
			map3["MemoryUseRate"] = map2["osMemoryUsage"]
			list[index] = map3
		} else if field == "port" {
			if (ver=="1.1"){
			  map3["@odata.id"] = map2["@odata.id"]
			  map3["position"] = map2["position"]
			  list[index] = map3
			}else{
			  list[index] = map2
			}
		} else if field == "mac" {
			map3["@odata.id"] = map2["@odata.id"]
			map3["id"] = map2["id"]
			list[index] = map3
		}
		index = index + 1
	}
	//log.Println("list making ")
	return list
}

//RegisterDevice 设备登记
func RegisterDevice(hostid string) {
	rs := GetrsManager()
	m := GetinstanceManager()

	/*
			 if rs.Deviceinlists(DEVICELISTKEY,hostid)==true{//请求的设备在设备列表中，重复出错 (如在redis-cli不使用del liveconnum，容易出现误判)
		        log.Printf("%s device exits in devicelist", hostid)
		        return
		     }*/

	if m.RegisterHost(rs, hostid+",0") == false {
		return
	}

	rs.Appendtodevicelists(DEVICELISTKEY, hostid)

}

//UnRegisterDevice 设备删除
func UnRegisterDevice(hostid string) {

	rs := GetrsManager()
	m := GetinstanceManager()

	/*
			 if rs.Deviceinlists(DEVICELISTKEY,hostid)==false{//请求的设备不在设备列表中，已经被删除 (如在redis-cli不使用del liveconnum，容易出现误判)
		        log.Printf("%s device is not in devicelist", hostid)
		        return
		     }*/

	if m.UnregisterHost(rs, hostid) == false {
		return
	}

	rs.Removefromdevicelists(DEVICELISTKEY, hostid)

}

//OperateDevice 打开或关闭设备
func OperateDevice(hostid string, optype string) {
	rs := GetrsManager()
	m := GetinstanceManager()
	if optype == "open" {
		m.OpenHost(rs, hostid)
	} else if optype == "close" {
		m.CloseHost(rs, hostid)
	}

}

//Searchvalue 分析members,找到uuid所在的submap 信息
func Searchvalue(info string, uuid string) string {
	map1 := make(map[string]interface{})
	var err = json.Unmarshal([]byte(info), &map1)
	if err != nil {
		log.Printf("json parse err:%s", info)
		return ""

	}
	//信息格式:
	/*"Members": [
	    {
	        "@odata.id": "/redfish/v1/rich/NetworkService/Hosts/xxxx/Devm/Ports/10GE1%2F17%2F1",
	        "position": "10GE2/17/1"
	    },
	    {
	        "@odata.id": "/redfish/v1/rich/NetworkService/Hosts/xxxx/Devm/Ports/10GE1%2F17%2F2",
	        "position": "10GE2/17/2"
	    }
	]*/
	submembermap := make(map[string]interface{})
	var value = (map1["Members"]) //这是关键词
	val, ok := value.([]interface{})
	if !ok {
		
		//log.Println("It's not ok for type members array")
		//str, ok2 := value.(string)
		//if ok2 {
		//   log.Println("members string:",str)
		//}
		return ""
	}

	for _, v2 := range val {
		switch vv := v2.(type) {
		case map[string]interface{}:

			//log.Println(vv);
			if vv["UUID"] == uuid {
				submembermap["uuidmap"] = val
				break
			}

		}
	}

	b, err := json.Marshal(submembermap)
	if err != nil {
		return ""
	}
	return string(b)
}

//Searchvaluefromlist list分析 ( for北向filter使用)
func Searchvaluefromlist(list List, filter string, value string) (List, int) {

	listlen := len(list)
	targetlist := make(List, listlen) //default len is 100
	var j = 0
	for i := 0; i < len(list); i++ {
		var valuemap = (list[i]) //这是关键词
		val, ok := valuemap.(map[string]interface{})
		if !ok {
			//log.Println("It's not ok for type list[i]")
			
			//str, ok2 := valuemap.(string)
			//if ok2 {
			//   log.Println("list string:",str)
			//}
			
			return nil, 0
		}

		if val[filter] == value {
			targetlist[j] = valuemap
			j = j + 1
		}

	}

	return targetlist, j
}
